Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ


Displaying Raw β€’ Download

core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/CoTDetailStripper.kt 65a1f5ce4d0e4b8eafcd0bbfccafd41dc6ee888a (65a1f5ce) Text, 7.94 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.takserver

T8b949e/**
* Removes bloat elements from the `<detail>` content of a CoT event before it is stuffed into a
* [org.meshtastic.proto.TAKPacketV2] `raw_detail` field for mesh transmission.
*
* # Why this exists
*
* A LoRa mesh packet has a hard payload limit of [org.meshtastic.proto.Constants.DATA_PAYLOAD_LEN] = 233 bytes for the
* entire encoded `Data` proto (portnum + payload + reply_id + emoji). Subtracting the wrapper overhead leaves roughly
* **~225 bytes** for the TAK wire payload, and the wire payload itself is `[1 byte dict-id flag][zstd-compressed
* TAKPacketV2 protobuf]`.
*
* ATAK emits CoT events with rich visual metadata that is **never useful over a mesh**: icon set paths, ARGB colors,
* shape geometry, archive flags, file references, etc. A typical `u-d-c-c` (user-drawn circle) event from ATAK is
* **800+ bytes of XML**, of which maybe 80 bytes are actually meaningful to a receiving node. Even with dictionary
* compression, the full payload overflows the MTU.
*
* This stripper deletes elements the receiving node can synthesize or ignore, leaving only the minimum needed to
* rebuild a usable `<event>` on the other side: who sent it, where they are, what team/role they're on, battery status,
* chat content, and the high-level CoT type (which rides separately on [TAKPacketV2.cot_type_id] /
* [TAKPacketV2.cot_type_str]).
*
* # What gets dropped
*
* **Cosmetic / rendering-only** (pure visual, no situational awareness value):
* - `<color .../>` β€” ARGB stroke/fill colors
* - `<strokeColor .../>`, `<strokeWeight .../>`, `<fillColor .../>` β€” shape styling
* - `<labels_on .../>` β€” label visibility toggle
* - `<usericon .../>` β€” icon set path (`COT_MAPPING_2525B/...`)
* - `<model .../>` β€” 3D model reference
*
* **Geometric detail** (we keep lat/lon on the event; shape primitives are too big):
* - `<shape>...</shape>` β€” ellipse/polyline/polygon geometry
* - `<height .../>`, `<height_unit .../>` β€” rendering hints
*
* **Resource references** (useless without the resource being reachable):
* - `<fileshare .../>` β€” file transfer references
* - `<__video .../>` β€” video stream URL
*
* **Flags and redundant metadata**:
* - `<archive/>` β€” "save to archive" flag
* - `<precisionlocation .../>` β€” redundant with the event's `<point>` attributes
* - `<tog .../>` β€” rectangle "toggle" UI state flag
* - `<_flow-tags_ .../>` β€” TAK Server routing metadata (server-to-server, not needed on mesh)
*
* # What gets preserved
*
* Anything the stripper doesn't explicitly match is passed through untouched. That includes all of the structured
* elements that the regular [CoTXmlParser] understands (contact, __group, status, track, remarks, __chat, chatgrp,
* link, uid, __serverdestination) plus any unknown extensions β€” better to over-preserve than silently drop something
* the receiving ATAK actually needs.
*
* # Whitespace
*
* All inter-element whitespace and indentation is collapsed. Whitespace inside text nodes (e.g. `<remarks>hello
* world</remarks>`) is preserved.
*
* # Not a real XML parser
*
* This is intentionally string/regex based, not DOM. The input is a small, well-formed fragment produced by ATAK's
* serializer, so a full parser is overkill β€” and we want this to be dependency-free so it can run on every KMP target
* without pulling in xmlutil for a one-off job. If ATAK starts emitting namespaced elements or embedded CDATA that
* tangles with these patterns, the stripper will leave them alone rather than corrupt the output, which is the safer
* failure mode.
*/
Tff7b72internal Tff7b72object T56d364CoTDetailStripper Tb4b4b4{

T8b949e/**
* Element names whose entire subtree (or self-closing tag) is removed.
*
* Order matters only for documentation. Each entry is tried against both the self-closing form `<name .../>` and
* the paired form `<name ...>...</name>`.
*/
Tff7b72private Tff7b72val Te6edf3STRIPPED_ELEMENTS Tff7b72=
Te6edf3listOfTb4b4b4(
T8b949e// Cosmetic / rendering
Ta5d6ff"Ta5d6ffcolorTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffstrokeColorTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffstrokeWeightTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffillColorTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflabels_onTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffusericonTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffmodelTa5d6ff"Tb4b4b4,
T8b949e// Geometric
Ta5d6ff"Ta5d6ffshapeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffheightTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffheight_unitTa5d6ff"Tb4b4b4,
T8b949e// Resource refs
Ta5d6ff"Ta5d6fffileshareTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff__videoTa5d6ff"Tb4b4b4,
T8b949e// Flags / redundant
Ta5d6ff"Ta5d6ffarchiveTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprecisionlocationTa5d6ff"Tb4b4b4,
T8b949e// Rectangle/polyline "toggle" UI flag, and TAK Server routing metadata.
T8b949e// The underscore-prefixed element names are legal XML identifiers ATAK uses
T8b949e// for internal state that receiving meshtastic nodes have no use for.
Ta5d6ff"Ta5d6fftogTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff_flow-tags_Ta5d6ff"Tb4b4b4,
Tb4b4b4)

T8b949e/**
* Pre-compiled regex list: for each stripped element, one pattern that matches either a self-closing tag or a
* paired open/close tag (non-greedy content).
*
* `[^>]*?` inside the open tag tolerates attribute quoting with both single and double quotes but bails if it
* encounters a `>` (so it won't accidentally swallow unrelated content).
*
* The leading `(?s)` inline flag is the KMP-portable equivalent of `RegexOption.DOT_MATCHES_ALL` β€” it lets `.`
* match newlines so a multi-line `<shape>...</shape>` subtree is captured in one pass.
* `RegexOption.DOT_MATCHES_ALL` itself is JVM-only and breaks the Kotlin/Native build.
*/
Tff7b72private Tff7b72val Te6edf3STRIPPED_ELEMENT_PATTERNSTb4b4b4: Te6edf3ListTff7b72<Te6edf3RegexTff7b72> Tff7b72=
Te6edf3STRIPPED_ELEMENTSTb4b4b4.Te6edf3map Tb4b4b4{ Te6edf3name Tff7b72-Tff7b72>
T8b949e// Escape the name in case it contains regex metacharacters (e.g. __video).
Tff7b72val Te6edf3escaped Tff7b72= Te6edf3RegexTb4b4b4.Te6edf3escapeTb4b4b4(Te6edf3nameTb4b4b4)
T8b949e// Matches:
T8b949e// <name/>
T8b949e// <name attr="..."/>
T8b949e// <name attr='...'>...content...</name>
Te6edf3RegexTb4b4b4(Ta5d6ff"""Ta5d6ff(?s)<Tffd700$Te6edf3escapedTa5d6ff(?:Ta5d6ff\Ta5d6ffs[^>]*?)?/>|<Tffd700$Te6edf3escapedTa5d6ff(?:Ta5d6ff\Ta5d6ffs[^>]*?)?>.*?</Tffd700$Te6edf3escapedTa5d6ff>Ta5d6ff"""Tb4b4b4)
Tb4b4b4}

T8b949e/** Matches whitespace between tags: `> \n <` β†’ `><`. */
Tff7b72private Tff7b72val Te6edf3INTER_TAG_WHITESPACE Tff7b72= Te6edf3RegexTb4b4b4(Ta5d6ff"""Ta5d6ff>Ta5d6ff\Ta5d6ffs+<Ta5d6ff"""Tb4b4b4)

T8b949e/** Collapse leading / trailing whitespace across the whole fragment. */
Tff7b72private Tff7b72val Te6edf3EDGE_WHITESPACE Tff7b72= Te6edf3RegexTb4b4b4(Ta5d6ff"""Ta5d6ff^Ta5d6ff\Ta5d6ffs+|Ta5d6ff\Ta5d6ffs+$Ta5d6ff"""Tb4b4b4)

T8b949e/**
* Strip bloat elements and normalize whitespace on an inner `<detail>` fragment.
*
* The input is assumed to be the concatenated children of `<detail>` β€” i.e., what
* [CoTXmlParser.extractDetailInnerXml] returns. It is NOT the full `<event>` or the `<detail>` wrapper itself.
*
* Returns an empty string if every element was stripped (so callers can treat "empty" and "nothing worth sending"
* uniformly).
*/
Tff7b72fun Td2a8ffstripTb4b4b4(Te6edf3detailInnerXmlTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657String Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3detailInnerXmlTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72return Ta5d6ff"Ta5d6ff"
Tff7b72var Te6edf3result Tff7b72= Te6edf3detailInnerXml
Tff7b72for Tb4b4b4(Te6edf3pattern Tff7b72in Te6edf3STRIPPED_ELEMENT_PATTERNSTb4b4b4) Tb4b4b4{
Te6edf3result Tff7b72= Te6edf3patternTb4b4b4.Te6edf3replaceTb4b4b4(Te6edf3resultTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tb4b4b4}
T8b949e// Collapse whitespace between remaining tags. Preserves whitespace inside
T8b949e// text nodes (e.g. <remarks>hello world</remarks>) because that whitespace
T8b949e// isn't bracketed by '>' and '<'.
Te6edf3result Tff7b72= Te6edf3INTER_TAG_WHITESPACETb4b4b4.Te6edf3replaceTb4b4b4(Te6edf3resultTb4b4b4, Ta5d6ff"Ta5d6ff><Ta5d6ff"Tb4b4b4)
Te6edf3result Tff7b72= Te6edf3EDGE_WHITESPACETb4b4b4.Te6edf3replaceTb4b4b4(Te6edf3resultTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72return Te6edf3result
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.04s